package com.wideplay.warp.security;

import com.google.inject.Inject;
import com.google.inject.Injector;
import net.jcip.annotations.GuardedBy;
import net.jcip.annotations.Immutable;

import javax.servlet.*;
import java.io.IOException;
import java.util.List;
import java.util.Collections;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created with IntelliJ IDEA.
 * On: 25/09/2007
 *
 * The core Warp-security class that pulls the service together (controls and routes dispatch from filters).
 *
 * An implementation of the local component butterfly pattern. It implements Filter only to enforce dispatch method signature.
 *
 * @author Dhanji R. Prasanna
 * @since 1.0
 */
@Immutable
class FilterChainDispatch implements Filter {
    private final List<Filter> filters;
    private final Injector injector;

    @GuardedBy("lock") //but only for construction and [external] destruction
    private static volatile FilterChainDispatch filterChain;    //volatile because reads do not happen under lock
    private static final Lock lock = new ReentrantLock();

    @Inject
    public FilterChainDispatch(@Filters List<Filter> filters, Injector injector) {
        try {
            lock.lock();

            this.filters = Collections.unmodifiableList(filters);
            this.injector = injector;

            if (null != filterChain)
                throw new IllegalStateException("Cannot register more than one Security service per web application");

            //store a singleton reference to this object, so it can be accessed by the servlet filterchain
            filterChain = this;
            
        } finally {
            lock.unlock();
        }
    }


    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain proceedingChain) throws IOException, ServletException {
        //first go down our chain
        //once done with our chain, continue down the webapp's chain        
        new FilterChainInvocation(filters, proceedingChain).doFilter(servletRequest, servletResponse);
    }

    static FilterChainDispatch getSecurityFilterChain() {
        return filterChain;
    }

    public void init(FilterConfig filterConfig) throws ServletException {
        //go down chain and initialize all our filters
        for (Filter filter : filters) {
            filter.init(filterConfig);
        }
    }

    public void destroy() {
        //go down chain and destroy all our filters
        for (Filter filter : filters) {
            filter.destroy();
        }
    }

    //a hack to allow an appl to reset the filter chain impl as though newly deployed
    static void reset() {
        try {
            lock.lock();

            filterChain = null;
        } finally {
            lock.unlock();
        }
    }
}
